/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Text File Class
 *      Represents and manages a text file and provides basic interactive operations
 * for text files.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Text;
using Adaptive.Foundation.Collections;

namespace Adaptive.Foundation
{
    /// <summary>
    /// Represents and manages a text file and provides basic interactive operations
    /// for text files.
    /// </summary>
    public class TextFile : DisposableObjectBase
    {
        /*--------------------------------------------------------------------------------
         * Public Event Definitions
         *------------------------------------------------------------------------------*/
        #region Public Event Definitions
        /// <summary>
        /// Occurs when a file read/load operation is started.
        /// </summary>
        public event EventHandler LoadStart;
        /// <summary>
        /// Occurs when a file read/load operation is completed.
        /// </summary>
        public event EventHandler LoadComplete;
        /// <summary>
        /// Occurs when an exception is encountered during a read or load operation.
        /// </summary>
        public event ExceptionEventHandler LoadException;
        /// <summary>
        /// Occurs when a file save/write operation is started.
        /// </summary>
        public event EventHandler SaveStart;
        /// <summary>
        /// Occurs when a file save/write operation is completed.
        /// </summary>
        public event EventHandler SaveComplete;
        /// <summary>
        /// Occurs when an exception is encountered during a write or save operation.
        /// </summary>
        public event ExceptionEventHandler SaveException;
        /// <summary>
        /// Occurs during a load or save operation to indicate the current progress.
        /// </summary>
        public event ProgressEventHandler IoOperationProgress;
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Name of the file.
        /// </summary>
        private string _fileName = string.Empty;
        /// <summary>
        /// Text contents.
        /// </summary>
        private StringCollection _contents;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public TextFile()
            : base()
        {
            _contents = new StringCollection();
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified
        /// file name.
        /// </summary>
        /// <param name="fileName">
        /// The path and name of the file.
        /// </param>
        public TextFile(string fileName)
        {
            _fileName = fileName;
            _contents = new StringCollection();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                Clear();
                if (_contents != null)
                    _contents.Dispose();
            }
            _contents = null;
            _fileName = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets a value indicating whether the file specified by <b>FileName</b> 
        /// currently exists.
        /// </summary>
        /// <value>
        /// <b>true</b> if the file exists; otherwise, <b>false</b>.
        /// </value>
        public bool Exists
        {
            get
            {
                bool returnValue = false;           //Return value.

                try
                {
                    returnValue = File.Exists(_fileName);
                }
                catch
                {
                }
                return returnValue;
            }
        }
        /// <summary>
        /// Gets the path in which the file is located.
        /// </summary>
        /// <value>
        /// The path portion of the <see cref="FileName"/> property.
        /// </value>
        public string Path
        {
            get
            {
                string returnValue = string.Empty;

                try
                {
                    returnValue = System.IO.Path.GetDirectoryName(_fileName);
                }
                catch
                {
                }
                return returnValue;
            }
        }
        /// <summary>
        /// Gets or sets the path and name of the text file.
        /// </summary>
        /// <value>
        /// A fully-qualified path and file name.
        /// </value>
        public string FileName
        {
            get { return _fileName; }
            set { _fileName = value; }
        }
        /// <summary>
        /// Gets or sets the contents of a text line at the specified index.
        /// </summary>
        /// <param name="index">The index of the line of texzt to be edited.</param>
        /// <value>
        /// A string containing the line of text.
        /// </value>
        public string this[int index]
        {
            get
            {
                string returnValue = string.Empty;

                if ((index >= 0) && (index < _contents.Count))
                    returnValue = _contents[index];

                return returnValue;
            }
            set
            {
                if ((index >= 0) && (index < _contents.Count))
                    _contents[index] = value;
            }
        }
        /// <summary>
        /// Gets the number of lines of text in the file.
        /// </summary>
        /// <value>
        /// The number of text lines that have been loaded.
        /// </value>
        public int LineCount
        {
            get { return _contents.Count; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Raises the <see cref="LoadStart"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnLoadStart(EventArgs e)
        {
            if (LoadStart != null)
                LoadStart(this, e);
        }
        /// <summary>
        /// Raises the <see cref="LoadComplete"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnLoadComplete(EventArgs e)
        {
            if (LoadComplete != null)
                LoadComplete(this, e);
        }
        /// <summary>
        /// Raises the <see cref="LoadException"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="ExceptionEventArgs"/> event arguments instance
        /// containing the <see cref="Exception"/> that was caught.
        /// </param>
        protected virtual void OnLoadException(ExceptionEventArgs e)
        {
            if (LoadException != null)
                LoadException(this, e);
        }
        /// <summary>
        /// Raises the <see cref="SaveStart"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnSaveStart(EventArgs e)
        {
            if (SaveStart != null)
                SaveStart(this, e);
        }
        /// <summary>
        /// Raises the <see cref="SaveComplete"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnSaveComplete(EventArgs e)
        {
            if (SaveComplete != null)
                SaveComplete(this, e);
        }
        /// <summary>
        /// Raises the <see cref="SaveException"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="ExceptionEventArgs"/> event arguments instance
        /// containing the <see cref="Exception"/> that was caught.
        /// </param>
        protected virtual void OnSaveException(ExceptionEventArgs e)
        {
            if (SaveException != null)
                SaveException(this, e);
        }
        /// <summary>
        /// Raises the <see cref="IoOperationProgress"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="ProgressEventArgs"/> event arguments instance 
        /// containing the current progress information.
        /// </param>
        protected virtual void OnIoOperationProgress(ProgressEventArgs e)
        {
            if (IoOperationProgress != null)
                IoOperationProgress(this, e);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Appends the specified text to the file as a new line of text.
        /// </summary>
        /// <param name="newLineOfText">
        /// The text to be appended.
        /// </param>
        public void AppendLine(string newLineOfText)
        {
            _contents.Add(newLineOfText);
        }
        /// <summary>
        /// Appends the specified text to the file.
        /// </summary>
        /// <remarks>
        /// This text may be parsed into nultiple text lines.
        /// </remarks>
        /// <param name="textToAppend">
        /// The text to be appended.
        /// </param>
        public void AppendText(string textToAppend)
        {
            string[] content = null;

            content = textToAppend.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            _contents.AddRange(content);
        }
        /// <summary>
        /// Clears the contents of the object instance without disposing of the instance.
        /// </summary>
        public void Clear()
        {
            _fileName = string.Empty;
            _contents.Clear();
        }

        
        /// <summary>
        /// Attempts the load the content of a text file into memory.
        /// </summary>
        /// <param name="fileName">
        /// The name of the file to be loaded.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Load(string fileName)
        {
            _fileName = fileName;
            return Load(fileName);
        }
        /// <summary>
        /// Attempts the load the content of a text file into memory.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Load()
        {
            FileStream file = null;                     //File access object.            
            StreamReader reader = null;                 //Text reader instance.
            ProgressEventArgs evArgs = null;            //Progress update event arguments.
            string lineOftext = null;                   //Line of text read from file.
            long current = 0;                           //Current position.
            long size = 0;                              //Total file size.
            bool returnValue = false;                   //Return value.

            //Do nothing if the file does not exist.
            if (Exists)
            {
                //Attempt to open the file.
                file = OpenFileForRead();

                //Raise the appropriate event(s).
                OnLoadStart(EventArgs.Empty);
                evArgs = new ProgressEventArgs();
                OnIoOperationProgress(evArgs);

                //Prepare the current content.
                try
                {
                    reader = new StreamReader(file);
                    size = file.Length;
                }
                catch (IOException ex)
                {
                    OnLoadException(new ExceptionEventArgs(ex));
                    reader = null;
                    file.Close();
                    file = null;
                }

                if (file != null)
                {
                    //Read and store each line of text in memory.
                    _contents.Clear();

                    do
                    {
                        //Attempt to read.
                        try
                        {
                            lineOftext = reader.ReadLine();
                            current = file.Position;
                            returnValue = true;
                        }
                        catch (IOException ex)
                        {
                            OnLoadException(new ExceptionEventArgs(ex));
                            lineOftext = null;
                        }

                        if (lineOftext != null)
                        {
                            //Store the data and raise the appropriate event.
                            _contents.Add(lineOftext);
                            current = file.Position;
                            evArgs.PercentComplete = (int)(((float)current / size) * 100);
                            OnIoOperationProgress(evArgs);
                        }

                    } while (lineOftext != null);

                    //Close file stream.
                    reader.Close();
                    file.Close();
                    file.Dispose();
                }

                //Raise final event.
                OnLoadComplete(EventArgs.Empty);
            }

            return returnValue;
        }
        /// <summary>
        /// Attempts the save the content of a text file to disk.
        /// </summary>
        /// <param name="fileName">
        /// The name of the file to be written.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Save(string fileName)
        {
            _fileName = fileName;
            return Save(fileName);
        }
        /// <summary>
        /// Attempts the save the content of a text file to disk.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, <b>false</b>.
        /// </returns>
        public bool Save()
        {
            FileStream file = null;                     //File access object.            
            StreamWriter writer = null;                 //Text writer instance.
            ProgressEventArgs evArgs = null;            //Progress update event arguments.
            long current = 0;                           //Current position.
            long size = 0;                              //Total file size.
            bool returnValue = false;                   //Return value.

            //Attempt to open the file.
            file = OpenFileForWrite();

            //Raise the appropriate event(s).
            OnSaveStart(EventArgs.Empty);
            evArgs = new ProgressEventArgs();
            OnIoOperationProgress(evArgs);

            //Prepare the current content.
            try
            {
                writer = new StreamWriter(file);
                size = _contents.Count;
            }
            catch (IOException ex)
            {
                OnSaveException(new ExceptionEventArgs(ex));
                writer = null;
                file.Close();
                file = null;
            }

            if (file != null)
            {
                //Read and store each line of text in memory.
                for (current = 0; current < size; current++)
                {
                    //Attempt to writer.
                    try
                    {
                        writer.WriteLine(_contents[(int)current]);
                        returnValue = true;
                        evArgs.PercentComplete = (int)(((float)current / size) * 100);
                        OnIoOperationProgress(evArgs);
                    }
                    catch (IOException ex)
                    {
                        OnSaveException(new ExceptionEventArgs(ex));
                    }

                }

                //Close file stream.
                writer.Close();
                file.Close();
                file.Dispose();
            }

            //Raise final event.
            OnSaveComplete(EventArgs.Empty);

            return returnValue;
        }                                       
        /// <summary>
        /// Gets all of the text content currently loaded.
        /// </summary>
        /// <returns>
        /// A string containing the content currently loaded into memory.
        /// </returns>
        public override string ToString()
        {
            StringBuilder builder = null;       //String concatenator.
            string returnValue = null;          //Return value.

            builder = new StringBuilder();
            _contents.MoveFirst();
            while (!_contents.Eof)
            {
                builder.Append(_contents.Value + "\r\n");
                _contents.MoveNext();
            }

            returnValue = builder.ToString();
            builder = null;
            return returnValue;

        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Static/Shared Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Static/Shared  Methods / Functions
        /// <summary>
        /// Loads the text content of the specified file into memory.
        /// </summary>
        /// <param name="fileName">
        /// The fully-qualified path and name of the file to be read.
        /// </param>
        /// <returns>
        /// A string containing the content of the file.
        /// </returns>
        public static string LoadText(string fileName)
        {
            TextFile fileToLoad = null;             //File handler.
            string returnValue = string.Empty;      //Return value.

            fileToLoad = new TextFile(fileName);
            if (fileToLoad.Load())
                returnValue = fileToLoad.ToString();

            fileToLoad.Dispose();
            return returnValue;
        }
        /// <summary>
        /// Saves the specified text content to the specified file.
        /// </summary>
        /// <param name="fileName">
        /// The fully-qualified path and name of the file to be read.
        /// </param>
        /// <param name="textToWrite">
        /// The text to be written to disk.
        /// </param>
        public static void SaveText(string fileName, string textToWrite)
        {
            TextFile fileToSave = null;             //File handler.
            string returnValue = string.Empty;      //Return value.

            fileToSave = new TextFile(fileName);
            fileToSave.AppendText(textToWrite);
            fileToSave.Save();
            fileToSave.Dispose();
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Opens a file for reading purposes.
        /// </summary>
        /// <returns>
        /// The <see cref="FileStream"/> instance, if successful; otherwise, returns <b>null</b>.
        /// </returns>
        public FileStream OpenFileForRead()
        {
            FileStream returnValue = null;          //Return value.

            try
            {
                returnValue = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
            }
            catch (IOException ex)
            {
                OnLoadException(new ExceptionEventArgs(ex));
                returnValue = null;
            }

            return returnValue;
        }
        /// <summary>
        /// Opens a file for writing purposes.
        /// </summary>
        /// <returns>
        /// The <see cref="FileStream"/> instance, if successful; otherwise, returns <b>null</b>.
        /// </returns>
        public FileStream OpenFileForWrite()
        {
            FileStream returnValue = null;          //Return value.

            try
            {
                if (File.Exists(_fileName))
                    File.Delete(_fileName);
            }
            catch
            { }

            try
            {
                returnValue = new FileStream(_fileName, FileMode.CreateNew, FileAccess.Write);
            }
            catch (IOException ex)
            {
                OnLoadException(new ExceptionEventArgs(ex));
                returnValue = null;
            }

            return returnValue;
        }
        #endregion
    }
}
